home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / cpp_libs / cmdline.lha / cmdline / src / cmd / fsm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-03  |  6.1 KB  |  258 lines

  1. //------------------------------------------------------------------------
  2. // ^FILE: fsm.c - implement a finite staet machine
  3. //
  4. // ^DESCRIPTION:
  5. //     This file implements a finite state machine tailored to the task of
  6. //     parsing syntax strings for command-line arguments.
  7. //
  8. // ^HISTORY:
  9. //    03/27/92    Brad Appleton    <brad@ssd.csd.harris.com>    Created
  10. //-^^---------------------------------------------------------------------
  11.  
  12. #include <stdlib.h>
  13. #include <iostream.h>
  14. #include <ctype.h>
  15. #include <string.h>
  16.  
  17. #include "fsm.h"
  18.  
  19.    // define the characters that have a "special" meaning
  20. enum {
  21.   c_LBRACE = '[',
  22.   c_RBRACE = ']',
  23.   c_ALT    = '|',
  24.   c_LIST   = '.',
  25. } ;
  26.  
  27.  
  28. //-------------------
  29. // ^FUNCTION: SyntaxFSM::skip - skip to the next token
  30. //
  31. // ^SYNOPSIS:
  32. //    SyntaxFSM::skip(input)
  33. //
  34. // ^PARAMETERS:
  35. //    const char * & input;
  36. //    -- the current "read" position in the syntax string.
  37. //
  38. // ^DESCRIPTION:
  39. //    Skip past all whitespace and past square braced (recording the
  40. //    current brace-nesting level and the number of balanced braces
  41. //    parsed).
  42. //
  43. // ^REQUIREMENTS:
  44. //    None.
  45. //
  46. // ^SIDE-EFFECTS:
  47. //    Updates "input" to point to the next token (or eos)
  48. //
  49. // ^RETURN-VALUE:
  50. //    None.
  51. //
  52. // ^ALGORITHM:
  53. //    Trivial.
  54. //-^^----------------
  55. void
  56. SyntaxFSM::skip(const char * & input) {
  57.    if ((! input) || (! *input))  return;
  58.  
  59.    while (isspace(*input))  ++input;
  60.    while ((*input == c_LBRACE) || (*input == c_RBRACE)) {
  61.       if (*input == c_LBRACE) {
  62.          ++lev;
  63.       } else {
  64.          if (lev > 0) {
  65.             ++nbpairs;
  66.          } else {
  67.             fsm_state = ERROR;
  68.             cerr << "too many '" << char(c_RBRACE) << "' characters." << endl;
  69.          }
  70.          --lev;
  71.       }
  72.       ++input;
  73.       while (isspace(*input))  ++input;
  74.    }//while
  75. }
  76.  
  77.  
  78. //-------------------
  79. // ^FUNCTION: SyntaxFSM::parse_token - parse a token
  80. //
  81. // ^SYNOPSIS:
  82. //    SyntaxFSM::parse_token(input)
  83. //
  84. // ^PARAMETERS:
  85. //    const char * & input;
  86. //    -- the current "read" position in the syntax string.
  87. //
  88. // ^DESCRIPTION:
  89. //    Get the next token from the input string.
  90. //
  91. // ^REQUIREMENTS:
  92. //    input should be non-NULL.
  93. //
  94. // ^SIDE-EFFECTS:
  95. //    Updates "input" to point to the next token (or eos)
  96. //
  97. // ^RETURN-VALUE:
  98. //    None.
  99. //
  100. // ^ALGORITHM:
  101. //    Trivial.
  102. //-^^----------------
  103. void
  104. SyntaxFSM::parse_token(const char * & input)
  105. {
  106.    while (*input && (! isspace(*input)) &&
  107.           (*input != c_LBRACE) && (*input != c_RBRACE) &&
  108.           ((*input != c_LIST) || (fsm_state == OPTION)))
  109.    {
  110.       ++input;
  111.    }
  112. }
  113.  
  114.  
  115. //-------------------
  116. // ^FUNCTION: SyntaxFSM::operator() - get a token
  117. //
  118. // ^SYNOPSIS:
  119. //    SyntaxFSM::operator()(input, token)
  120. //
  121. // ^PARAMETERS:
  122. //    const char * & input;
  123. //    -- the current "read" position in the syntax string.
  124. //
  125. //    token_t & token;
  126. //    -- where to place the token that we will find.
  127. //
  128. // ^DESCRIPTION:
  129. //    Get the next token from the input string.
  130. //
  131. // ^REQUIREMENTS:
  132. //    None.
  133. //
  134. // ^SIDE-EFFECTS:
  135. //    - updates "input" to point to the next token (or eos)
  136. //    - updates "token" to be the token that we found
  137. //
  138. // ^RETURN-VALUE:
  139. //    0 if we are in a non-FINAL state; non-zero otherwise..
  140. //
  141. // ^ALGORITHM:
  142. //    It gets complicated so follow along.
  143. //-^^----------------
  144. int
  145. SyntaxFSM::operator()(const char * & input, token_t & token)
  146. {
  147.    token.set(NULL, 0);
  148.  
  149.       // if inout is NULL or empty - then we are finished
  150.    if ((! input) || (! *input)) {
  151.       if (lev) {
  152.          cerr << "not enough '" << char(c_RBRACE) << "' characters." << endl ;
  153.          fsm_state = ERROR;
  154.          return  (fsm_state != FINAL);
  155.       } else {
  156.          fsm_state = FINAL;
  157.          return  (fsm_state != FINAL);
  158.       }
  159.    }
  160.  
  161.    skip(input);  // skip whitespace
  162.  
  163.    const char * start = input;
  164.  
  165.       // the token we are to parse depends on what state we are in
  166.    switch(fsm_state) {
  167.    case  START :
  168.       // We are parsing either an option-character name or a value.
  169.       // If it is an option-character name, the character that stops
  170.       // the input scan will be c_ALT.
  171.       //
  172.       if (*input != c_ALT)  ++input;
  173.       if (*input == c_ALT) {
  174.          fsm_state = OPTION;
  175.          if (start != input)  token.set(start, 1);
  176.       } else {
  177.          parse_token(input);
  178.          fsm_state = VALUE;
  179.          token.set(start, (input - start));
  180.       }
  181.       ++ntoks;
  182.       break;
  183.  
  184.    case  OPTION :
  185.       // We parsed an option-character already so we had better see a keyword
  186.       // name this time around.
  187.       //
  188.       start = ++input;  // skip past the '|' character
  189.       if (! isspace(*input)) {
  190.          parse_token(input);
  191.          token.set(start, (input - start));
  192.       }
  193.       fsm_state = KEYWORD;
  194.       ++ntoks;
  195.       break;
  196.  
  197.    case  KEYWORD :
  198.       // We parsed a keyword already - if anything is here then it better be a
  199.       // value name.
  200.       //
  201.       if (*input) {
  202.          parse_token(input);
  203.          fsm_state = VALUE;
  204.          token.set(start, (input - start));
  205.          ++ntoks;
  206.       } else {
  207.          fsm_state = FINAL;
  208.       } 
  209.       break;
  210.  
  211.    case  VALUE :
  212.       // We already parsed a value name - all that could possibly be left
  213.       // (that we be valid) is an ellipsis ("...") indicating a list.
  214.       //
  215.       if (! *input) {
  216.          fsm_state = FINAL;
  217.       } else if (::strncmp(input, "...", 3) == 0) {
  218.          fsm_state = LIST;
  219.          token.set(input, 3);
  220.          input += 3;
  221.          ++ntoks;
  222.       } else {
  223.          fsm_state = ERROR;
  224.          cerr << "unexpected token \"" << input << "\"." << endl ;
  225.       }
  226.       break;
  227.  
  228.    case  LIST :
  229.       // We already parsed an ellipsis, there better not be anything left
  230.       if (! *input) {
  231.          fsm_state = FINAL;
  232.       } else {
  233.          fsm_state = ERROR;
  234.          cerr << "unexpected token \"" << input << "\"." << endl ;
  235.       }
  236.       break;
  237.  
  238.    case  ERROR :
  239.    case  FINAL :
  240.    default :
  241.       break;
  242.    }
  243.  
  244.    if (fsm_state == FINAL) {
  245.       skip(input);
  246.       if ((! *input) && lev) {
  247.          cerr << "not enough '" << char(c_RBRACE) << "' characters." << endl ;
  248.          fsm_state = ERROR;
  249.       } else if (*input) {
  250.          cerr << "unexpected token \"" << input << "\"." << endl ;
  251.          fsm_state = ERROR;
  252.       }
  253.    }
  254.  
  255.    return  (fsm_state != FINAL);
  256. }
  257.  
  258.